/**
 * 数据库配置类
 */
package indi.zjh.dp.db;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;
import org.dom4j.DocumentException;

import com.mysql.jdbc.Driver;

import indi.zjh.dp.error.InvalidDataException;
import indi.zjh.dp.startup.Constant;
import indi.zjh.dp.type.GSet;
import indi.zjh.dp.util.XMLUtil;
import indi.zjh.dp.vc.Version;
import indi.zjh.dp.verify.CharEmptyRule;
import indi.zjh.dp.verify.CharNotEmptyRule;
import indi.zjh.dp.verify.CharSetRule;
import indi.zjh.dp.verify.Check;
import indi.zjh.dp.verify.StringSetRule;
import indi.zjh.dp.verify.ValueRangeRule;

public class Configuration {
	private static Logger logger = Logger.getLogger(Configuration.class);
	
	private static int dbkind = 0; //0-mysql, 1-sqlite
	
	public class MySQLConf
	{
		public String driver;
		public String url;
		public String user;
		public String password;
		public long timeout;
		public boolean isWritable = true;
		public int poolSize = 0; // 如果是0, 不用连接池, 大于0, 代表连接池最大的连接数
	}
	
	public class RedisConf
	{
		public boolean enable = true;
		public String host;
		public String auth;
		public int maxConnection = 128;
		public int maxIdleConnection = 32;
		public int connectionWaitTimeout = 10000;
		public int connectionTimeout = 10000;
	}
	
	public class ZkConf
	{
		public String host;
		public int sessionTimeout;
		public int connectTimeout;
		public int reconnectTimeout;
		public int reconnectTryCount;
	}
	
	public class HttpConf
	{
		public int port = 80;
	}
	
	public class BusiConf
	{
		public String checkoutPath;
		public List<String> packages = new ArrayList<String>();
	}
	
	
	protected MySQLConf mysqlConf = new MySQLConf();
	protected RedisConf redisConf = new RedisConf();
	protected ZkConf zkConf = new ZkConf();
	protected HttpConf httpConf = new HttpConf();
	protected BusiConf busiConf = new BusiConf();
	
	protected SessionFactory sFactory = null;
	protected static Configuration instance = null;
	
	// 当前的业务版本
	protected Version version;
	
	private Configuration()
	{
		
	}
	
	public static Configuration getInstance() throws DocumentException, Exception
	{
		getSessionFactory();
		return instance;
	}
	
	/**
	 * 加载初始化配置参数
	 * @throws Exception 
	 * @throws DocumentException 
	 */
	public void load() throws DocumentException, Exception
	{
		try
		{
			// MySQL配置初始化
			this.mysqlConf.driver = XMLUtil.getString(Constant.CONF_FILE, Constant.DATABASE_DRIVER);
			Check.data(this.mysqlConf.driver).desc("数据库driver配置非法").rule(new CharNotEmptyRule());
			this.mysqlConf.url = XMLUtil.getString(Constant.CONF_FILE, Constant.DATABASE_URL);
			Check.data(this.mysqlConf.url).desc("数据库连接字符串配置非法").rule(new CharNotEmptyRule());
			this.mysqlConf.user = XMLUtil.getString(Constant.CONF_FILE, Constant.DATABASE_USER);
			Check.data(this.mysqlConf.user).desc("数据库账户名称配置非法").rule(new CharNotEmptyRule());
			this.mysqlConf.password = XMLUtil.getString(Constant.CONF_FILE, Constant.DATABASE_PASSWORD);
			Check.data(this.mysqlConf.password).desc("数据库账户密码配置非法").rule(new CharNotEmptyRule());
			String timeout = XMLUtil.getString(Constant.CONF_FILE, Constant.DATABASE_TIMEOUT);
			Check.data(timeout).desc("数据库连接超时配置非法").rule(new ValueRangeRule(50000, 500000));
			this.mysqlConf.timeout = Long.parseLong(timeout);
			String connectionPoolVal = XMLUtil.getString(Constant.CONF_FILE, Constant.DATABASE_CONNECTION_POOL);
			Check.data(connectionPoolVal).desc("数据库连接池配置非法").rule(new ValueRangeRule(0, 8));
			this.mysqlConf.poolSize = Integer.parseInt(connectionPoolVal);
			
			// Redis配置初始化
			String value = XMLUtil.getString(Constant.CONF_FILE, "/Business/redis/enable");
			Check.data(value).desc("Redis使能开关配置错误").rule(new StringSetRule(GSet.createStringSet().addString("0").addString("1")));
			if(value.equals("0"))
				this.redisConf.enable = false;
			else
				this.redisConf.enable = true;
			value = XMLUtil.getString(Constant.CONF_FILE, "/Business/redis/host");
			Check.data(value).desc("Redis连接服务器配置非法").rule(new CharNotEmptyRule());
			this.redisConf.host = value;
			value = XMLUtil.getString(Constant.CONF_FILE, "/Business/redis/max-connection");
			Check.data(value).desc("Redis最大连接数配置非法").rule(new ValueRangeRule(24, 512));
			this.redisConf.maxConnection = Integer.parseInt(value);
			value = XMLUtil.getString(Constant.CONF_FILE, "/Business/redis/max-idle-connection");
			Check.data(value).desc("Redis最大空闲连接数配置非法").rule(new ValueRangeRule(1, 128));
			this.redisConf.maxIdleConnection = Integer.parseInt(value);
			value = XMLUtil.getString(Constant.CONF_FILE, "/Business/redis/connection-wait-timeout");
			Check.data(value).desc("Redis连接会话超时配置非法").rule(new ValueRangeRule(1000, 500000));
			this.redisConf.connectionWaitTimeout = Integer.parseInt(value);
			value = XMLUtil.getString(Constant.CONF_FILE, "/Business/redis/connection-timeout");
			Check.data(value).desc("Redis连接超时配置非法").rule(new ValueRangeRule(1000, 500000));
			this.redisConf.connectionTimeout = Integer.parseInt(value);
			
			this.redisConf.auth = XMLUtil.getString(Constant.CONF_FILE, "/Business/redis/auth");
			
			this.busiConf.checkoutPath = XMLUtil.getString(Constant.CONF_FILE, "/Business/business-repository-path");
			
			// zookeeper 配置
			value = XMLUtil.getString(Constant.SERVER_CONF_FILE, "/Server/zookeeper-server/host");
			Check.data(value).desc("Zookeeper连接服务器配置非法").rule(new CharNotEmptyRule());
			this.zkConf.host = value;
			value = XMLUtil.getString(Constant.SERVER_CONF_FILE, "/Server/zookeeper-server/session-timeout");
			Check.data(value).desc("Zookeeper会话超时配置非法").rule(new ValueRangeRule(1000, 500000));
			this.zkConf.sessionTimeout = Integer.parseInt(value);
			value = XMLUtil.getString(Constant.SERVER_CONF_FILE, "/Server/zookeeper-server/connect-timeout");
			Check.data(value).desc("Zookeeper连接超时配置非法").rule(new ValueRangeRule(1000, 500000));
			this.zkConf.connectTimeout = Integer.parseInt(value);
			value = XMLUtil.getString(Constant.SERVER_CONF_FILE, "/Server/zookeeper-server/reconnect-timeout");
			Check.data(value).desc("Zookeeper重连超时配置非法").rule(new ValueRangeRule(1000, 500000));
			this.zkConf.reconnectTimeout = Integer.parseInt(value);
			value = XMLUtil.getString(Constant.SERVER_CONF_FILE, "/Server/zookeeper-server/reconnect-trycount");
			Check.data(value).desc("Zookeeper重连次数配置非法").rule(new ValueRangeRule(1, 16));
			this.zkConf.reconnectTryCount = Integer.parseInt(value);
			
			
			// http 配置
			value = XMLUtil.getString(Constant.SERVER_CONF_FILE, "/Server/access/port");
			Check.data(value).desc("HTTP端口配置非法").rule(new ValueRangeRule(80, 65535));
			this.httpConf.port = Integer.parseInt(value);
			
			// 确定数据库类型
			if(this.mysqlConf.driver.equals("com.mysql.jdbc.Driver"))
			{
				dbkind = 0;
			}
			else
			{
				dbkind = 1;
			}
		}
		catch(InvalidDataException e)
		{
			logger.error(e.getMessage());
			e.printStackTrace();
			System.exit(1);
		}
	}
		
	public static SessionFactory getSessionFactory() throws DocumentException, Exception
	{
		if(instance == null)
		{
			instance = new Configuration();
			instance.load();
			Class.forName(instance.mysqlConf.driver).newInstance();
		}
		if(instance.sFactory == null)
		{
			instance.sFactory = new SessionFactory(instance);
		}
		return instance.sFactory;
	}
	
	/**
	 * 设置当前业务版本
	 * @param v
	 */
	public void setVersion(Version v)
	{
		this.version = v;
	}
	
	/**
	 * 获取当前业务版本
	 * @return
	 */
	public Version getVersion()
	{
		return this.version;
	}
	
	public MySQLConf getMySQLConf()
	{
		return this.mysqlConf;
	}
	
	public RedisConf getRedisConf()
	{
		return this.redisConf;
	}
	
	public HttpConf getHttpConf()
	{
		return this.httpConf;
	}
	
	public BusiConf getBusiConf()
	{
		return this.busiConf;
	}
	
	public ZkConf getZkConf()
	{
		return this.zkConf;
	}
	
	public boolean isWritable()
	{
		return this.mysqlConf.isWritable;
	}
	
	public void lock()
	{
		this.mysqlConf.isWritable = false;
	}
	
	public void unlock()
	{
		this.mysqlConf.isWritable = true;
	}
	
	/**
	 * 判断是否为mysql数据库
	 * @return
	 */
	public static boolean isMysql()
	{
		if(dbkind == 0)
			return true;
		return false;
	}
}
